package com.server.tools;

import com.google.protobuf.CodedOutputStream;
import com.google.protobuf.InvalidProtocolBufferException;
import com.proto_java.PBChatHistory;
import com.proto_java.proto_main.ProtobufPackage;
import com.server.mongodb.domain.MGChatHistory;
import com.server.tools.http_exception.BusinessException;
import io.netty.buffer.ByteBuf;
import io.netty.buffer.ByteBufOutputStream;
import io.netty.buffer.Unpooled;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * protobuf类的工具类
 */
@Slf4j
public class ProtoBufTools {

    /**
     * 将字节数组转为protobuf外壳对象
     * 这样就调方法即可,不必每个请求都重复写转换
     * @param bytes 收到的字节数组
     * @return protobuf外壳对象
     */
    public static ProtobufPackage.PackageInfo get(byte[] bytes){
        ProtobufPackage.PackageInfo classInfo = null;
        try {
            classInfo = ProtobufPackage.PackageInfo.parseFrom(bytes);
        } catch (InvalidProtocolBufferException e) {
            throw new BusinessException(e.getMessage(), Code.TYPE_ERR);
        }
        return classInfo;
    }

    /**
     * !!!!!注意！！使用这个方法后一定记得显示关闭此方法返回的byteBuf资源！否则会内存泄漏！！！！！
     * !!!!! 除非你后面用了writeAndFlush，就不必关了，它会自动关闭这个资源 !!!!
     * 返回一个封装好的ByteBuf类
     * 用于返回给前端请求结果,websocket用
     * @param protobuf 需要封装的数据包
     * @return 封装好的ByteBuf
     */
    public static ByteBuf getResponseClass(ProtobufPackage.PackageInfo protobuf){

        // 创建需要大小的byteBuf
        ByteBuf byteBuf = Unpooled.buffer(protobuf.getSerializedSize());
        try {
            // protobuf工具类，用于将protobuf编码为二进制
            CodedOutputStream codedOutputStream =
                    // 它必须用一个流对象来构建，这里使用了ByteBufOutputStream并传入一个byteBuf作为内存空间
                    // ByteBufOutputStream可以理解为一个工具,使用它将CodedOutputStream转换后的二进制写入byteBuf
                    // 它们两个不持有任何资源,只是用来将protobuf转换并写入byteBuf中,所以也不必释放什么资源
                    CodedOutputStream.newInstance(new ByteBufOutputStream(byteBuf));

            // 将protobuf对象通过CodedOutputStream转为二进制然后用ByteBufOutputStream写入byteBuf中(就是在套娃)
            protobuf.writeTo(codedOutputStream);

            // 刷新一下,因为writeTo并不会刷新流
            codedOutputStream.flush();
        } catch (IOException e) {
            // 发生异常时关闭资源,也就是让计数-1
            byteBuf.release();
            log.error("响应消息构建异常",e);
        }
        // 记得在调用它的方法里关闭返回的byteBuf资源！！！
        // 除非你后面用了writeAndFlush，就不必关了，它会自动关闭这个资源
        return byteBuf;
    }

    /**
     * !!!!!注意！！使用这个方法后一定记得显示关闭此方法返回的byteBuf资源！否则会内存泄漏！！！！！
     * !!!!! 除非你后面用了writeAndFlush，就不必关了，它会自动关闭这个资源 !!!!
     * 返回一个封装好的ByteBuf类
     * 用于返回给前端请求结果,websocket用
     * @param code 状态码
     * @param msg 状态描述
     * @param taskId 任务编号
     * @return 封装好的ByteBuf
     */
    public static ByteBuf getResponseClass(Integer code, String msg,Integer taskId,Integer callbackId){

        ProtobufPackage.PackageInfo protobuf = ProtobufPackage.PackageInfo.newBuilder()
                .setCode(code)
                .setMsg(msg)
                .setTaskId(taskId)
                .setCallbackId(callbackId)
                .build();

        // 创建需要大小的byteBuf
        ByteBuf byteBuf = Unpooled.buffer(protobuf.getSerializedSize());
        try {
            // protobuf工具类，用于将protobuf编码为二进制
            CodedOutputStream codedOutputStream =
                    // 它必须用一个流对象来构建，这里使用了ByteBufOutputStream并传入一个byteBuf作为内存空间
                    // ByteBufOutputStream可以理解为一个工具,使用它将CodedOutputStream转换后的二进制写入byteBuf
                    // 它们两个不持有任何资源,只是用来将protobuf转换并写入byteBuf中,所以也不必释放什么资源
                    CodedOutputStream.newInstance(new ByteBufOutputStream(byteBuf));

            // 将protobuf对象通过CodedOutputStream转为二进制然后用ByteBufOutputStream写入byteBuf中(就是在套娃)
            protobuf.writeTo(codedOutputStream);

            // 刷新一下,因为writeTo并不会刷新流
            codedOutputStream.flush();
        } catch (IOException e) {
            // 发生异常时关闭资源,也就是让计数-1
            byteBuf.release();
            log.error("基础响应消息构建异常",e);
        }
        // 记得在调用它的方法里关闭返回的byteBuf资源！！！
        // 除非你后面用了writeAndFlush，就不必关了，它会自动关闭这个资源
        return byteBuf;
    }


    /**
     * 返回一个封装好的消息类
     * 用于返回给前端请求结果,http用
     * @param code 状态码
     * @param msg 状态描述
     * @return 封装好的字节数组
     */
    public static byte[] getResponseClass(Integer code,String msg){
        return ProtobufPackage.PackageInfo.newBuilder()
                .setCode(code)
                .setMsg(msg)
                .build().toByteArray();
    }


    /**
     * PBChatHistory.ChatHistory构造器,接收一个MGChatHistory
     * 返回组装好的单条聊天信息
     * @param p 数据库查询到的单条数据
     * @return 返回一个PBChatHistory.ChatHistory
     */
    public static PBChatHistory.ChatHistory getOneChatHistory(MGChatHistory p){

        if(p.getGroupUid()!=null){
            return PBChatHistory.ChatHistory.newBuilder()
                    .setData(p.getData())
                    .setIsImage(p.isImage())
                    .setSendUserUid(p.getSendUserUid())
                    .setSendUserHeadImage(PublicUtils.getFile(p.getSendUserHeadImage()))
                    .setSendUserName(p.getSendUserName())
                    .setSendDate(PublicUtils.DateConvertString(p.getSendDate(),"yyyy-MM-dd HH:mm:ss"))
                    .setIsRead(p.isRead())
                    .setGroupUid(p.getGroupUid())
                    .setGroupName(p.getGroupName())
                    .setGroupHeadImage(PublicUtils.getFile(p.getGroupHeadImage())).build();
        }else{
            return PBChatHistory.ChatHistory.newBuilder()
                    .setData(p.getData())
                    .setIsImage(p.isImage())
                    .setSendUserUid(p.getSendUserUid())
                    .setSendUserHeadImage(PublicUtils.getFile(p.getSendUserHeadImage()))
                    .setSendUserName(p.getSendUserName())
                    .setGetUserName(p.getGetUserName())
                    .setGetUserUid(p.getGetUserUid())
                    .setGetUserHeadImage(PublicUtils.getFile(p.getGetUserHeadImage()))
                    .setSendDate(PublicUtils.DateConvertString(p.getSendDate(),"yyyy-MM-dd HH:mm:ss"))
                    .setIsRead(p.isRead()).build();
        }

    }



}
